using System;
using System.Collections.Generic;
using System.Text;
using System.Security.Cryptography;
using System.Web;
using System.Net;
using System.IO;
using System.Xml;
using MJB.UI.ContentEditor.Document;

namespace MJB.UI.ContentEditor
{
    /// <summary>
    /// This interface defines an object that is able to communicate to
    /// the XML management service and return data.
    /// </summary>
    public class XmlManagementService
    {
        #region Member Variables

        /// <summary>
        /// These are the editor settings.
        /// </summary>
        private EditorSettings mEditorSettings;

        /// <summary>
        /// This is the encrypted user name.
        /// </summary>
        private string mEncryptedUserName;

        /// <summary>
        /// This is the encrypted password.
        /// </summary>
        private string mEncryptedPassword;

        #endregion
        #region Constructor

        /// <summary>
        /// Creates the XML management service and loads the values from the registry.
        /// </summary>
        /// <param name="editorSettings"></param>
        public XmlManagementService()
        {
            // Get the user settings from the registry:
            mEditorSettings = new EditorSettings();
            mEditorSettings.LoadFromRegistry();

            // Just encrypt the strings here:
            MD5 md5 = MD5.Create();

            byte[] userNameBytes = Encoding.UTF8.GetBytes(mEditorSettings.UserName);
            byte[] encryptedUserNameBytes = md5.ComputeHash(userNameBytes);

            StringBuilder encryptedUserNameBuilder = new StringBuilder();
            foreach(byte encryptedUserNameByte in encryptedUserNameBytes)
                encryptedUserNameBuilder.Append(String.Format("{0:x2}", encryptedUserNameByte));

            mEncryptedUserName = encryptedUserNameBuilder.ToString();

            byte[] passwordBytes = Encoding.UTF8.GetBytes(mEditorSettings.Password);
            byte[] encryptedPasswordBytes = md5.ComputeHash(passwordBytes);

            StringBuilder encryptedPasswordBuilder = new StringBuilder();
            foreach(byte encryptedPasswordByte in encryptedPasswordBytes)
                encryptedPasswordBuilder.Append(String.Format("{0:x2}", encryptedPasswordByte));

            mEncryptedPassword = encryptedPasswordBuilder.ToString();

        }

        #endregion
        #region Public Methods

        /// <summary>
        /// Returns a list of file nodes that are each of the directories in the remote
        /// folders path.
        /// </summary>
        /// <param name="path">The path to search from.  The root directory should be blank.</param>
        /// <returns>A list of sub directories.</returns>
        public virtual List<string> GetDirectories(string path)
        {
            HttpWebRequest httpRequest = (HttpWebRequest)WebRequest.Create(mEditorSettings.XmlManagementURL);
            httpRequest.Method = "POST";
            httpRequest.ContentType = "application/x-www-form-urlencoded";

            StringBuilder requestBuilder = new StringBuilder();
            requestBuilder.Append("UserName=");
            requestBuilder.Append(mEncryptedUserName);
            requestBuilder.Append("&");

            requestBuilder.Append("Password=");
            requestBuilder.Append(mEncryptedPassword);
            requestBuilder.Append("&");

            requestBuilder.Append("Command=GetDirectories&");
            requestBuilder.Append("Path=");
            requestBuilder.Append(path);

            string requestString = requestBuilder.ToString();
            httpRequest.ContentLength = requestString.Length;

            using(StreamWriter streamWriter = new StreamWriter(httpRequest.GetRequestStream()))
            {
                streamWriter.Write(requestString);
            }

            List<string> subDirectories = new List<string>();

            HttpWebResponse httpWebResponse = (HttpWebResponse)httpRequest.GetResponse();
            using(XmlTextReader textReader = new XmlTextReader(httpWebResponse.GetResponseStream()))
            {
                XmlDocument responseDoc = new XmlDocument();
                responseDoc.Load(textReader);

                foreach(XmlElement directoryNode in responseDoc.DocumentElement.ChildNodes)
                    subDirectories.Add(directoryNode.InnerText);

            }

            return subDirectories;

        }

        /// <summary>
        /// Returns a list of files under a particular directory.
        /// </summary>
        /// <param name="path">The path to return files for.</param>
        /// <returns>A list of files under a directory.</returns>
        public virtual List<string> GetFiles(string path)
        {
            HttpWebRequest httpRequest = (HttpWebRequest)WebRequest.Create(mEditorSettings.XmlManagementURL);
            httpRequest.Method = "POST";
            httpRequest.ContentType = "application/x-www-form-urlencoded";

            StringBuilder requestBuilder = new StringBuilder();
            requestBuilder.Append("UserName=");
            requestBuilder.Append(mEncryptedUserName);
            requestBuilder.Append("&");

            requestBuilder.Append("Password=");
            requestBuilder.Append(mEncryptedPassword);
            requestBuilder.Append("&");

            requestBuilder.Append("Command=GetFiles&");
            requestBuilder.Append("Path=");
            requestBuilder.Append(path);

            string requestString = requestBuilder.ToString();
            httpRequest.ContentLength = requestString.Length;

            using(StreamWriter streamWriter = new StreamWriter(httpRequest.GetRequestStream()))
            {
                streamWriter.Write(requestString);
            }

            List<string> files = new List<string>();

            HttpWebResponse httpWebResponse = (HttpWebResponse)httpRequest.GetResponse();
            using(XmlTextReader textReader = new XmlTextReader(httpWebResponse.GetResponseStream()))
            {
                XmlDocument responseDoc = new XmlDocument();
                responseDoc.Load(textReader);

                foreach(XmlElement fileNode in responseDoc.DocumentElement.ChildNodes)
                    files.Add(fileNode.InnerText);
            }

            return files;

        }

        /// <summary>
        /// Returns the serialized xml file from the remote server.
        /// </summary>
        /// <param name="path"></param>
        /// <returns></returns>
        public virtual IDocumentReader GetFile(string path)
        {
            // Create the GetFile request:
            HttpWebRequest httpRequest = (HttpWebRequest)WebRequest.Create(mEditorSettings.XmlManagementURL);
            httpRequest.Method = "POST";
            httpRequest.ContentType = "application/x-www-form-urlencoded";

            StringBuilder requestBuilder = new StringBuilder();
            requestBuilder.Append("UserName=");
            requestBuilder.Append(mEncryptedUserName);
            requestBuilder.Append("&");

            requestBuilder.Append("Password=");
            requestBuilder.Append(mEncryptedPassword);
            requestBuilder.Append("&");

            requestBuilder.Append("Command=GetFile&");
            requestBuilder.Append("Path=");
            requestBuilder.Append(path);

            string requestString = requestBuilder.ToString();
            httpRequest.ContentLength = requestString.Length;

            using(StreamWriter streamWriter = new StreamWriter(httpRequest.GetRequestStream()))
                streamWriter.Write(requestString);

            // This will read the response stream into memory:
            HttpWebResponse httpResponse = (HttpWebResponse)httpRequest.GetResponse();
            string s = httpResponse.ContentEncoding;
            string responseString = String.Empty;
            using(StreamReader streamReader = new StreamReader(httpResponse.GetResponseStream()))
                responseString = streamReader.ReadToEnd();

            XmlReader xmlReader = null;
            MemoryStream memoryStream = null;
            IDocumentReader reader = null;

            try
            {
                byte[] fileBytes = Encoding.UTF8.GetBytes(responseString);

                // Get the reader:
                memoryStream = new MemoryStream(fileBytes);
                reader = DocumentReaderFactory.GetReader(memoryStream);

                // Create an xml text reader and deserialize the document:
                memoryStream = new MemoryStream(fileBytes);
                xmlReader = new XmlTextReader(memoryStream);
                reader.ReadXml(xmlReader);

            }
            finally
            {
                if(memoryStream != null)
                    memoryStream.Close();

                if(xmlReader != null)
                    xmlReader.Close();
            }

            return reader;

        }


        /// <summary>
        /// Creates a file on the remote server.
        /// </summary>
        /// <param name="path">The path of the file.</param>
        /// <param name="content">The content of the file to send.</param>
        public virtual void CreateFile(string path)
        {
            // Create the GetFile request:
            HttpWebRequest httpRequest = (HttpWebRequest)WebRequest.Create(mEditorSettings.XmlManagementURL);
            httpRequest.Method = "POST";
            httpRequest.ContentType = "application/x-www-form-urlencoded";

            StringBuilder requestBuilder = new StringBuilder();
            requestBuilder.Append("UserName=");
            requestBuilder.Append(mEncryptedUserName);
            requestBuilder.Append("&");

            requestBuilder.Append("Password=");
            requestBuilder.Append(mEncryptedPassword);
            requestBuilder.Append("&");

            requestBuilder.Append("Command=CreateFile&");

            requestBuilder.Append("Path=");
            requestBuilder.Append(path);

            string requestString = requestBuilder.ToString();
            httpRequest.ContentLength = requestString.Length;

            using(StreamWriter streamWriter = new StreamWriter(httpRequest.GetRequestStream()))
                streamWriter.Write(requestString);

            // This will make sure the operation succeeded:
            HttpWebResponse httpResponse = (HttpWebResponse)httpRequest.GetResponse();
            using(XmlTextReader xmlReader = new XmlTextReader(httpResponse.GetResponseStream()))
            {
                XmlDocument responseDoc = new XmlDocument();
                responseDoc.Load(xmlReader);

                if(responseDoc.DocumentElement.ChildNodes[0].LocalName == "Error")
                    throw new Exception("There was an error creating the file on the remote server: " + responseDoc.DocumentElement.ChildNodes[0].InnerText);

            }

        }


        /// <summary>
        /// Creates a directory on the remote server.
        /// </summary>
        /// <param name="path">The path of the directory.</param>
        public virtual void CreateDirectory(string path)
        {
            // Create the CreateDirectory request:
            HttpWebRequest httpRequest = (HttpWebRequest)WebRequest.Create(mEditorSettings.XmlManagementURL);
            httpRequest.Method = "POST";
            httpRequest.ContentType = "application/x-www-form-urlencoded";

            StringBuilder requestBuilder = new StringBuilder();
            requestBuilder.Append("UserName=");
            requestBuilder.Append(mEncryptedUserName);
            requestBuilder.Append("&");

            requestBuilder.Append("Password=");
            requestBuilder.Append(mEncryptedPassword);
            requestBuilder.Append("&");

            requestBuilder.Append("Command=CreateDirectory&");

            requestBuilder.Append("Path=");
            requestBuilder.Append(path);

            string requestString = requestBuilder.ToString();
            httpRequest.ContentLength = requestString.Length;

            using(StreamWriter streamWriter = new StreamWriter(httpRequest.GetRequestStream()))
                streamWriter.Write(requestString);

            // This will make sure the operation succeeded:
            HttpWebResponse httpResponse = (HttpWebResponse)httpRequest.GetResponse();
            using(XmlTextReader xmlReader = new XmlTextReader(httpResponse.GetResponseStream()))
            {
                XmlDocument responseDoc = new XmlDocument();
                responseDoc.Load(xmlReader);

                if(responseDoc.DocumentElement.ChildNodes[0].LocalName == "Error")
                    throw new Exception("There was an error creating the directory on the remote server: " + responseDoc.DocumentElement.ChildNodes[0].InnerText);

            }

        }

        /// <summary>
        /// Sends an updated version of a file at a specified path to the server.
        /// </summary>
        /// <param name="path">The path of the file.</param>
        /// <param name="content">The content of the file to send.</param>
        public virtual void UpdateFile(string path, IDocumentReader documentReader)
        {
            // First, lets serialize the reader into a string:
            StringBuilder stringBuilder = new StringBuilder();
            XmlWriterSettings settings = new XmlWriterSettings();
            settings.Encoding = Encoding.UTF8;
            settings.CheckCharacters = false;
            settings.OmitXmlDeclaration = true;
            settings.Indent = true;

            using(XmlWriter xmlWriter = XmlWriter.Create(stringBuilder, settings))
                documentReader.WriteXml(xmlWriter);

            // Create the UpdateFile request:
            HttpWebRequest httpRequest = (HttpWebRequest)WebRequest.Create(mEditorSettings.XmlManagementURL);
            httpRequest.Method = "POST";
            httpRequest.ContentType = "application/x-www-form-urlencoded";

            StringBuilder requestBuilder = new StringBuilder();
            requestBuilder.Append("UserName=");
            requestBuilder.Append(mEncryptedUserName);
            requestBuilder.Append("&");

            requestBuilder.Append("Password=");
            requestBuilder.Append(mEncryptedPassword);
            requestBuilder.Append("&");

            requestBuilder.Append("Command=UpdateFile&");

            requestBuilder.Append("Path=");
            requestBuilder.Append(path);
            requestBuilder.Append("&");

            requestBuilder.Append("Content=");
            requestBuilder.Append(HttpUtility.UrlEncode(stringBuilder.ToString()));

            string requestString = requestBuilder.ToString();
            httpRequest.ContentLength = requestString.Length;

            using(StreamWriter streamWriter = new StreamWriter(httpRequest.GetRequestStream()))
                streamWriter.Write(requestString);

            // This will read the response stream into memory:
            HttpWebResponse httpResponse = (HttpWebResponse)httpRequest.GetResponse();
            using(XmlTextReader xmlReader = new XmlTextReader(httpResponse.GetResponseStream()))
            {
                XmlDocument responseDoc = new XmlDocument();
                responseDoc.Load(xmlReader);

                if(responseDoc.DocumentElement.ChildNodes[0].LocalName == "Error")
                    throw new Exception("There was an error updating the file on the remote server: " + responseDoc.DocumentElement.ChildNodes[0].InnerText);

            }

        }

        /// <summary>
        /// Removes a file on the remote server.
        /// </summary>
        /// <param name="path"></param>
        public virtual void DeleteFile(string path)
        {
            // Create the DeleteFile request:
            HttpWebRequest httpRequest = (HttpWebRequest)WebRequest.Create(mEditorSettings.XmlManagementURL);
            httpRequest.Method = "POST";
            httpRequest.ContentType = "application/x-www-form-urlencoded";

            StringBuilder requestBuilder = new StringBuilder();
            requestBuilder.Append("UserName=");
            requestBuilder.Append(mEncryptedUserName);
            requestBuilder.Append("&");

            requestBuilder.Append("Password=");
            requestBuilder.Append(mEncryptedPassword);
            requestBuilder.Append("&");

            requestBuilder.Append("Command=DeleteFile&");

            requestBuilder.Append("Path=");
            requestBuilder.Append(path);

            string requestString = requestBuilder.ToString();
            httpRequest.ContentLength = requestString.Length;

            using(StreamWriter streamWriter = new StreamWriter(httpRequest.GetRequestStream()))
                streamWriter.Write(requestString);

            // This will read the response stream into memory:
            HttpWebResponse httpResponse = (HttpWebResponse)httpRequest.GetResponse();
            using(XmlTextReader xmlReader = new XmlTextReader(httpResponse.GetResponseStream()))
            {
                XmlDocument responseDoc = new XmlDocument();
                responseDoc.Load(xmlReader);

                if(responseDoc.DocumentElement.ChildNodes[0].LocalName == "Error")
                    throw new Exception("There was an error deleting the file on the remote server: " + responseDoc.DocumentElement.ChildNodes[0].InnerText);

            }

        }

        /// <summary>
        /// Removes a directory on the remote server.
        /// </summary>
        /// <param name="path"></param>
        public virtual void DeleteDirectory(string path)
        {
            // Create the DeleteDirectory request:
            HttpWebRequest httpRequest = (HttpWebRequest)WebRequest.Create(mEditorSettings.XmlManagementURL);
            httpRequest.Method = "POST";
            httpRequest.ContentType = "application/x-www-form-urlencoded";

            StringBuilder requestBuilder = new StringBuilder();
            requestBuilder.Append("UserName=");
            requestBuilder.Append(mEncryptedUserName);
            requestBuilder.Append("&");

            requestBuilder.Append("Password=");
            requestBuilder.Append(mEncryptedPassword);
            requestBuilder.Append("&");

            requestBuilder.Append("Command=DeleteDirectory&");

            requestBuilder.Append("Path=");
            requestBuilder.Append(path);

            string requestString = requestBuilder.ToString();
            httpRequest.ContentLength = requestString.Length;

            using(StreamWriter streamWriter = new StreamWriter(httpRequest.GetRequestStream()))
                streamWriter.Write(requestString);

            // This will read the response stream into memory:
            HttpWebResponse httpResponse = (HttpWebResponse)httpRequest.GetResponse();
            using(XmlTextReader xmlReader = new XmlTextReader(httpResponse.GetResponseStream()))
            {
                XmlDocument responseDoc = new XmlDocument();
                responseDoc.Load(xmlReader);

                if(responseDoc.DocumentElement.ChildNodes[0].LocalName == "Error")
                    throw new Exception("There was an error deleting the directory on the remote server: " + responseDoc.DocumentElement.ChildNodes[0].InnerText);

            }

        }

        #endregion
    }

}
